﻿using OpenCvSharp;
using RemoteSensingImageDirectServer.Models.Request.Listener;
using System.Drawing;
using RemoteSensingImageDirectServer.Untils.Pool;
using RemoteSensingImageDirectServer.Untils.Source;
using RemoteSensingImageDirectServer.Untils.ScopeSpace;
using OSGeo.OGR;
using RemoteSensingImageDirectServer.Untils.Tool;
using OSGeo.GDAL;

namespace RemoteSensingImageDirectServer.Untils.Extensions
{
    public static class MatExtensions
    {
        public static ImageEncodingParam[] CV_EncodeWEBP_Params = new ImageEncodingParam[]
        {
            new ImageEncodingParam(ImwriteFlags.WebPQuality,60)
        };

        public static ImageEncodingParam[] CV_EncodePNGJPEG_Params = new ImageEncodingParam[]
        {
            new ImageEncodingParam(ImwriteFlags.JpegQuality,75),
            new ImageEncodingParam(ImwriteFlags.PngCompression,6),
        };


        public static Mat Mask(this Mat mat, IRSIMapSource source, RasterEnvelope env, bool ismask, int imageXSize, int imageYSize, int bandCount)
        {
            if (!ismask) return mat;

            Mat pTile = null;
            var maskfile = source.GetMaskFilename();
            if (string.IsNullOrWhiteSpace(maskfile) || !System.IO.File.Exists(maskfile)) return mat;

            var maskDs = Ogr.Open(maskfile, 0);
            if (maskDs == null) return mat;

            if (maskDs.GetLayerCount() == 0) return mat;
            var maskLayer = maskDs.GetLayerByIndex(0);

            var maskName = (source.GetKey() + env.ToString()).ToMD5String();

            try
            {
                var maskDataset = GdalTool.CreateMEMDataset(maskName, imageXSize, imageYSize, 1, OSGeo.GDAL.DataType.GDT_Byte);
                maskDataset.SetSpatialRef(source.GetDstDataset().GetSpatialRef());
                maskDataset.SetRasterEnvelope(env);

                var maskByte = maskDataset.BurnMask(maskLayer, 1);
                var maskMat = new Mat(imageYSize, imageXSize, MatType.CV_8UC1, maskByte);


                if (bandCount == 3)
                {
                    pTile = new Mat();
                    mat.CopyTo(pTile, maskMat);
                }
                else
                {
                    mat.SetTo(255, maskMat);
                    pTile = mat;
                }

                return pTile;
            }
            catch (Exception ms)
            {
                return mat;
            }
            finally
            {
                Gdal.Unlink(maskName);
                maskLayer.Dispose();
                maskLayer = null;
                maskDs.Free();
            }
        }

        public static Mat Gamma(this Mat mat, double val)
        {
            if (val == 1.0000f) return mat;

            if (val <= 0)
                val = 0.05d;
            else if (val >= 2)
                val = 1.95d;

            var lut = MatStretchPool.Instance().GetLut(val);

            return mat.LUT(lut);
        }

        /// <summary>
        /// 查找Mat中是否存在黑色的像元值
        /// </summary>
        /// <param name="mat">Mat对象</param>
        /// <returns>存在则返回true，不存在则返回false</returns>
        public static bool FindBlack(this Mat mat)
        {
            int count = mat.Rows * mat.Cols;
            if (mat.Type().Value == MatType.CV_8UC1)
            {
                return false;
            }
            else if (mat.Type().Value == MatType.CV_8UC3)
            {
                for (int k = 0; k < count; k++)
                {
                    int i = k / mat.Cols;
                    int j = k % mat.Cols;
                    var value = mat.Get<Vec3b>(i, j);
                    if (value.Item0 != 0 || value.Item1 != 0 || value.Item2 != 0)
                        continue;

                    return true;
                }
            }
            else
            {
                for (int k = 0; k < count; k++)
                {
                    int i = k / mat.Cols;
                    int j = k % mat.Cols;
                    var value = mat.At<Vec4b>(i, j);

                    if (value.Item0 != 0 || value.Item1 != 0 || value.Item2 != 0)
                        continue;

                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 将某个颜色设置为Mat的透明色(让此颜色在Mat中透明)
        /// </summary>
        /// <param name="mat">Mat对象</param>
        /// <param name="color">背景色</param>
        /// <returns>返回一个Mat对象</returns>
        public static Mat MakeTransparent(this Mat mat, Color color)
        {
            int count = mat.Rows * mat.Cols;
            if (mat.Type().Value == MatType.CV_8UC1)
            {
                for (int k = 0; k < count; k++)
                {
                    int i = k / mat.Cols;
                    int j = k % mat.Cols;
                    var value = mat.Get<Vec2b>(i, j);

                    if (value.Item0 != 0)
                        continue;

                    value.Item0 = 255;

                    mat.Set(i, j, value);

                }
            }
            else if (mat.Type().Value == MatType.CV_8UC4)
            {
                for (int k = 0; k < count; k++)
                {
                    int i = k / mat.Cols;
                    int j = k % mat.Cols;
                    var value = mat.At<Vec4b>(i, j);

                    if ((value.Item0 + value.Item1 + value.Item2 != 0) && (value.Item0 != color.B || value.Item1 != color.G || value.Item2 != color.R))
                        continue;

                    value.Item3 = 0;

                    mat.Set(i, j, value);

                }
            }
            else if (mat.Type().Value == MatType.CV_8UC3)
            {
                var new_Mat = new Mat(mat.Height, mat.Width, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));

                for (int k = 0; k < count; k++)
                {
                    int i = k / mat.Cols;
                    int j = k % mat.Cols;
                    var value = mat.Get<Vec3b>(i, j);
                    var new_value = new_Mat.At<Vec4b>(i, j);
                    new_value.Item0 = value.Item0;
                    new_value.Item1 = value.Item1;
                    new_value.Item2 = value.Item2;
                    new_value.Item3 = 255;
                    if ((value.Item0 + value.Item1 + value.Item2 != 0) && (value.Item0 != color.B || value.Item1 != color.G || value.Item2 != color.R))
                    {
                        new_Mat.Set(i, j, new_value);
                    }
                }
                mat = new_Mat;
            }
            return mat;
        }

        /// <summary>
        /// 将Mat对象转换为二进制流对象
        /// </summary>
        /// <param name="mat">Mat对象</param>
        /// <param name="isPng">图片是否满幅，非满幅则为PNG</param>
        /// <param name="imageStyle">图片的转换格式参数</param>
        /// <returns>返回一个二进制流数组</returns>
        public static byte[] ToStream(this Mat mat, IImageListener imageListener, out string imageFormat)
        {
            bool TRANSPARENT = imageListener.Transparent;
            bool ISMASK = imageListener.IsMask;
            string BGCOLOR = imageListener.BGColor;
            string FORMAT = imageListener.Format;

            var backColor = ColorTranslator.FromHtml(BGCOLOR);
            byte[] photo;
            if (ISMASK)//如果掩膜的话直接掩膜就ok
            {
                if (FORMAT.ToLower().Equals("image/webp"))
                {
                    mat = mat.MakeTransparent(backColor);
                    //Cv2.ImDecode(src_Mat, ImreadModes.AnyColor);
                    Cv2.ImEncode(".webp", mat, out photo, CV_EncodeWEBP_Params);//png
                    imageFormat = "image/webp";
                }
                else
                {
                    mat = mat.MakeTransparent(backColor);
                    Cv2.ImDecode(mat, ImreadModes.AnyColor);
                    Cv2.ImEncode(".png", mat, out photo, CV_EncodePNGJPEG_Params);//png
                    imageFormat = "image/png";
                }
            }
            ///当设置图片为透明背景时，直接返回png图片，优先度高
            else if (TRANSPARENT)
            {
                if (FORMAT.ToLower().Equals("image/webp"))
                {
                    mat = mat.MakeTransparent(backColor);
                    Cv2.ImEncode(".webp", mat, out photo, CV_EncodeWEBP_Params);//png
                    imageFormat = "image/webp";
                }
                else
                {
                    mat = mat.MakeTransparent(backColor);
                    Cv2.ImEncode(".png", mat, out photo, CV_EncodePNGJPEG_Params);
                    imageFormat = "image/png";
                }
            }
            else if (FORMAT.ToLower().Equals("image/jpeg"))
            {
                Cv2.ImEncode(".jpeg", mat, out photo, CV_EncodePNGJPEG_Params);
                imageFormat = "image/jpeg";
            }
            else if (FORMAT.ToLower().Equals("image/png"))
            {
                mat = mat.MakeTransparent(backColor);
                Cv2.ImEncode(".png", mat, out photo, CV_EncodePNGJPEG_Params);
                imageFormat = "image/png";
            }
            //当没有设置为mask，并且没有设置为透明背景时，则进行以下判断
            else if (FORMAT.ToLower().Equals("image/mixed"))
            {
                //判断是否存在背景值   && backColor.Equals(Color.Black)且背景值为黑色
                if (mat.FindBlack())
                {
                    mat = mat.MakeTransparent(backColor);
                    Cv2.ImEncode(".png", mat, out photo, CV_EncodePNGJPEG_Params);
                    imageFormat = "image/png";
                }
                else
                {
                    Cv2.ImEncode(".jpeg", mat, out photo, CV_EncodePNGJPEG_Params);
                    imageFormat = "image/jpeg";
                }
            }
            else if (FORMAT.ToLower().Equals("image/webp"))
            {
                //判断是否存在背景值   && backColor.Equals(Color.Black)且背景值为黑色
                if (mat.FindBlack())
                {
                    mat = mat.MakeTransparent(backColor);
                    Cv2.ImEncode(".webp", mat, out photo, CV_EncodeWEBP_Params);
                    imageFormat = "image/webp";
                }
                else
                {
                    Cv2.ImEncode(".webp", mat, out photo, CV_EncodeWEBP_Params);
                    imageFormat = "image/webp";
                }
            }
            else
            {
                Cv2.ImEncode(".jpeg", mat, out photo, CV_EncodePNGJPEG_Params);
                imageFormat = "image/jpeg";
            }
            return photo;
        }
    }
}
